#include "trap.h"
#include "gate.h"
#include "printk.h"
#include "lib.h"
#include "ptrace.h"

void sys_vector_init() {
    set_trap_gate(0, 1, divide_error);
    set_trap_gate(1, 1, debug);
    set_intr_gate(2, 1, nmi);
    set_system_gate(3, 1, int3);
    set_system_gate(4, 1, overflow);
    set_system_gate(5, 1, bounds);
    set_trap_gate(6, 1, undefined_opcode);
    set_trap_gate(7, 1, dev_not_available);
    set_trap_gate(8, 1, double_fault);
    set_trap_gate(9, 1, coprocessor_segment_overrun);
    set_trap_gate(10, 1, invalid_TSS);
    set_trap_gate(11, 1, segment_not_present);
    set_trap_gate(12, 1, stack_segment_fault);
    set_trap_gate(13, 1, general_protection);
    set_trap_gate(14, 1, page_fault);
    // 15 Intel reserved. Do not use
    set_trap_gate(16, 1, x87_FPU_error);
    set_trap_gate(17, 1, alignment_check);
    set_trap_gate(18, 1, machine_check);
    set_trap_gate(19, 1, SIMD_exception);
    set_trap_gate(20, 1, virtualization_exception);
}

void do_divide_error(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_divide_error(0), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}

void do_debug(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_debug(1), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}

void do_nmi(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_nmi(2), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}

void do_int3(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_int3(3), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}

void do_overflow(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_overflow(4), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}

void do_bounds(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_bounds(5), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}

void do_undefined_opcode(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_undefined_opcode(6), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}

void do_dev_not_available(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_dev_not_available(7), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}

void do_double_fault(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_double_fault(8), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}

void do_coprocessor_segment_overrun(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_coprocessor_segment_overrun(9), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}

void do_invalid_TSS(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_invalid_TSS(10), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);

    if (error_code & 0x1) {
        color_printk(RED, BLACK, "The exception occurred during delivery of an event external to the program, such as an interrupt or an earlier exception.\n");
    }

    if (error_code & 0x2) {
        color_printk(RED, BLACK, "Refers to a gate descriptor in the IDT;\n");
    } else {
        color_printk(RED, BLACK, "Refers to a descriptor in the GDT or the current LDT;\n");
    }

    if ((error_code & 0x2) == 0) {
        if (error_code & 0x4) {
            color_printk(RED, BLACK, "Refers to a segment or gate descriptor in the LDT;\n");
        } else {
            color_printk(RED, BLACK, "Refers to a descriptor in the current GDT;\n");
        }
    }

    color_printk(RED, BLACK, "Segment Selector Index:%#010x\n", error_code & 0xfff8);

    while(1);
}

void do_segment_not_present(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_segment_not_present(11), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);

    if (error_code & 0x1) {
        color_printk(RED, BLACK, "The exception occurred during delivery of an event external to the program, such as an interrupt or an earlier exception.\n");
    }

    if (error_code & 0x2) {
        color_printk(RED, BLACK, "Refers to a gate descriptor in the IDT;\n");
    } else {
        color_printk(RED, BLACK, "Refers to a descriptor in the GDT or the current LDT;\n");
    }

    if ((error_code & 0x2) == 0) {
        if (error_code & 0x4) {
            color_printk(RED, BLACK, "Refers to a segment or gate descriptor in the LDT;\n");
        } else {
            color_printk(RED, BLACK, "Refers to a descriptor in the current GDT;\n");
        }
    }

    color_printk(RED, BLACK, "Segment Selector Index:%#010x\n", error_code & 0xfff8);

    while(1);
}

void do_stack_segment_fault(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_stack_segment_fault(12), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);

    if (error_code & 0x1) {
        color_printk(RED, BLACK, "The exception occurred during delivery of an event external to the program, such as an interrupt or an earlier exception.\n");
    }

    if (error_code & 0x2) {
        color_printk(RED, BLACK, "Refers to a gate descriptor in the IDT;\n");
    } else {
        color_printk(RED, BLACK, "Refers to a descriptor in the GDT or the current LDT;\n");
    }

    if ((error_code & 0x2) == 0) {
        if (error_code & 0x4) {
            color_printk(RED, BLACK, "Refers to a segment or gate descriptor in the LDT;\n");
        } else {
            color_printk(RED, BLACK, "Refers to a descriptor in the current GDT;\n");
        }
    }

    color_printk(RED, BLACK, "Segment Selector Index:%#010x\n", error_code & 0xfff8);

    while(1);
}

void do_general_protection(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_general_protection(13), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);

    if (error_code & 0x1) {
        color_printk(RED, BLACK, "The exception occurred during delivery of an event external to the program, such as an interrupt or an earlier exception.\n");
    }

    if (error_code & 0x2) {
        color_printk(RED, BLACK, "Refers to a gate descriptor in the IDT;\n");
    } else {
        color_printk(RED, BLACK, "Refers to a descriptor in the GDT or the current LDT;\n");
    }

    if ((error_code & 0x2) == 0) {
        if (error_code & 0x4) {
            color_printk(RED, BLACK, "Refers to a segment or gate descriptor in the LDT;\n");
        } else {
            color_printk(RED, BLACK, "Refers to a descriptor in the current GDT;\n");
        }
    }

    color_printk(RED, BLACK, "Segment Selector Index:%#010x\n", error_code & 0xfff8);

    while(1);
}

void do_page_fault(st_pt_regs *regs, unsigned long error_code) {
    unsigned long cr2 = 0;

    __asm__ __volatile__("movq %%cr2, %0":"=r"(cr2)::"memory");

    color_printk(RED, BLACK, "do_page_fault(14), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);

    if (error_code & 0x1) {
        color_printk(RED, BLACK, "Page Not-Present,\t");
    }

    if (error_code & 0x2) {
        color_printk(RED, BLACK, "Write cause fault,\t");
    } else {
        color_printk(RED, BLACK, "Read cause fault,\t");
    }

    if (error_code & 0x4) {
        color_printk(RED, BLACK, "Fault in user(3)\t");
    } else {
        color_printk(RED, BLACK, "Fault in supervisor(0,1,2)\t");
    }

    if (error_code & 0x8) {
        color_printk(RED, BLACK, ", Reserved bit cause fault\t");
    }

    if (error_code & 0x10) {
        color_printk(RED, BLACK, ", Instruction fetch cause fault");
    }

    color_printk(RED, BLACK, "\nCR2:%#018lx\n", cr2);

    while(1);
}

void do_x87_FPU_error(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_x87_FPU_error(16), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}

void do_alignment_check(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_alignment_check(17), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}

void do_machine_check(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_machine_check(18), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}

void do_SIMD_exception(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_SIMD_exception(19), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}

void do_virtualization_exception(st_pt_regs *regs, unsigned long error_code) {
    color_printk(RED, BLACK, "do_Virtualization_exception(20), ERROR_CODE:%#018lx, RSP:%#018lx, RIP:%#018lx\n", error_code, regs->rsp, regs->rip);
    while(1);
}